package full_synchronizer

import (
	"errors"
	"fmt"
	"go-kvsql/kv_table"
	"go-kvsql/sql_table"
	"go-kvsql/sync_log_table"
	"go-kvsql/sync_log_table/sync_log"
	"go-kvsql/sync_log_table/sync_log_array"
	"go-kvsql/task_calculator"
	"log"
	"reflect"
	"runtime"
	"strconv"
)

type FullSynchronizer struct {
	kvTable            *kv_table.KvTable
	sqlTable           *sql_table.SqlTable
	syncLogTable       *sync_log_table.SyncLogTable
	taskCount          int
	taskChannelArray   []chan sync_log.SyncLog
	arrayCountDesigner *task_calculator.TaskCalculator
	resultCountChannel chan string
}

func Execute(kvTable *kv_table.KvTable, sqlTable *sql_table.SqlTable,
	syncLogTable *sync_log_table.SyncLogTable, tableType reflect.Type) error {

	syncLogArray, err := syncLogTable.SortArray(tableType)
	if err != nil {
		log.Fatalln("syncLogTable.SortArray-error: ", err)
	}
	syncLogCount := len(syncLogArray)
	arrayCountDesigner := task_calculator.NewTaskCalculator(runtime.NumCPU() * 2)
	taskCount := arrayCountDesigner.TaskCount
	taskChannelArray := make([]chan sync_log.SyncLog, taskCount)
	fullSynchronizer := FullSynchronizer{kvTable: kvTable, sqlTable: sqlTable, syncLogTable: syncLogTable,
		taskCount: taskCount, taskChannelArray: taskChannelArray, arrayCountDesigner: arrayCountDesigner,
		resultCountChannel: make(chan string, syncLogCount)}

	for i := 0; i < taskCount; i++ {
		fullSynchronizer.taskChannelArray[i] = make(chan sync_log.SyncLog, 10_0000)
		go fullSynchronizer.syncTask(i)
	}
	go fullSynchronizer.fullSyncTask(tableType, syncLogArray, syncLogCount)

	errorText := ""
	for i := 0; i < syncLogCount; i++ {
		resultText := <-fullSynchronizer.resultCountChannel
		errorText = errorText + resultText
		if len(errorText) > 0 {
			errorText = errorText + "\n"
		}
	}
	if len(errorText) > 0 {
		return errors.New(errorText)
	}
	if fullSynchronizer.syncLogTable.ContainsData() {
		strCount := strconv.Itoa(fullSynchronizer.syncLogTable.Count())
		return errors.New("fullSynchronizer error, remain sync log count: " + strCount)
	}
	return nil
}

func (this FullSynchronizer) fullSyncTask(tableType reflect.Type,
	syncLogArray sync_log_array.SyncLogArray, syncLogCount int) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("fullSyncTask-error:", err)
			go this.fullSyncTask(tableType, syncLogArray, syncLogCount)
		}
	}()

	for i := 0; i < syncLogCount; i++ {
		syncLog := syncLogArray[i]
		index := this.arrayCountDesigner.ComputeIndex(syncLog.Key)
		this.taskChannelArray[index] <- syncLog
	}
}

func (this FullSynchronizer) syncTask(taskNumber int) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("syncTask-error:", err)
			go this.syncTask(taskNumber)
		}
	}()

	for {
		syncLog := <-this.taskChannelArray[taskNumber]
		var err error
		if syncLog.LogType == sync_log.Del {
			err = this.kvTable.Del(syncLog.Key)
		} else {
			err = this.kvTable.Set(syncLog.Key, syncLog.ValueBytes)
		}
		if err != nil {
			this.resultCountChannel <- err.Error()
			continue
		}
		err = this.sqlTable.SyncOperate(syncLog)
		if err != nil {
			this.resultCountChannel <- err.Error()
		} else {
			this.resultCountChannel <- ""
		}
	}
}
